package tum.in.rost.pp;

import weka.core.Instance;
import weka.core.Instances;
import weka.classifiers.Classifier;
import weka.classifiers.functions.SMO;
import weka.classifiers.functions.supportVector.RBFKernel;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;

public class SVM_MileStone1 {

	/**
	 * @param trainData
	 * @param crossTrainData
	 */
	
	public static SMO SMOTOCHECK=new SMO();
	
	public void trainSVM(String data1, String data2, int bestModel, String data3) {
		double[] crossTrainResults = new double[6];
		double[] crossTrainResult;
		SMO[] smo = new SMO[6];
		try {
			Instances dataPart1 = new Instances(new FileReader(data1));
			Instances dataPart2 = new Instances(new FileReader(data2));
			Instances dataPart3 = new Instances(new FileReader(data3));
			dataPart1.setClassIndex(dataPart1.numAttributes() - 1);
			dataPart2.setClassIndex(dataPart2.numAttributes() - 1);
			dataPart3.setClassIndex(dataPart3.numAttributes() - 1);
			// method parameter 0: linearKernel, 1:RBFKernel
			long l = System.currentTimeMillis();
			System.out.println();
			smo[0] = setSVMParameters(1);
			System.out.println("Time taken for model to run: " + (System.currentTimeMillis() - l));
			retrievModel();
			smo[0].buildClassifier(dataPart1);
			crossTrainResult = crossTrainAndEvaluate(smo[0], dataPart2, false);
			crossTrainResults[0] = crossTrainResult[0];
			crossTrainResult = crossTrainAndEvaluate(smo[0], dataPart3, false);
			crossTrainResults[1] = crossTrainResult[0];

			smo[1] = setSVMParameters(1);
			smo[1].buildClassifier(dataPart2);
			crossTrainResult = crossTrainAndEvaluate(smo[1], dataPart1, false);
			crossTrainResults[2] = crossTrainResult[0];
			crossTrainResult = crossTrainAndEvaluate(smo[1], dataPart3, false);
			crossTrainResults[3] = crossTrainResult[0];

			smo[2] = setSVMParameters(1);
			smo[2].buildClassifier(dataPart3);
			crossTrainResult = crossTrainAndEvaluate(smo[1], dataPart1, false);
			crossTrainResults[4] = crossTrainResult[0];
			crossTrainResult = crossTrainAndEvaluate(smo[1], dataPart2, false);
			crossTrainResults[5] = crossTrainResult[0];

			double max = 0.0;
			int bestVersion = -1;
			double[] testResult = null;

			for (int i = 0; i < 6; i++) {
				if (crossTrainResults[i] > max) {
					max = crossTrainResults[i];
					bestVersion = i;

				}
			}

			System.out.println("Best Version: " + bestVersion + "Maximum Q2 crossTrain: " + max);
			switch (bestVersion) {
			case 0:
				testResult = crossTrainAndEvaluate(smo[0], dataPart3, true);
				break;
			case 1:
				testResult = crossTrainAndEvaluate(smo[0], dataPart2, true);
				break;
			case 2:
				testResult = crossTrainAndEvaluate(smo[1], dataPart3, true);
				break;
			case 3:
				testResult = crossTrainAndEvaluate(smo[1], dataPart1, true);
				break;
			case 4:
				testResult = crossTrainAndEvaluate(smo[2], dataPart2, true);
				break;
			case 5:
				testResult = crossTrainAndEvaluate(smo[2], dataPart1, true);
				break;
			default:
				break;
			}
			if (testResult != null) {
				System.out.println("TruePositive: " + (int) testResult[0]);
				System.out.println("TrueNegative: " + (int) testResult[1]);
				System.out.println("FalseNegative: " + (int) testResult[2]);
				System.out.println("FalsePositive: " + (int) testResult[3]);
				System.out.println("correctly classified Instances: " + (int) testResult[4]);
				System.out.println("Incorrectly classified Instances: " + (int) testResult[5]);
				System.out.println(" Q2: " + testResult[6]);
				System.out.println(" Acc(TM): " + testResult[7]);
				System.out.println(" Acc(non-TM): " + testResult[8]);
				System.out.println(" Cov(TM): " + testResult[9]);
				System.out.println(" Cov(non-TM): " + testResult[10]);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public SMO retrievModel() throws FileNotFoundException, ClassNotFoundException, IOException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(InstanceDataParser.SMO_MODEL_SERIALIZED_FILE));
		SMO smo = (SMO) ois.readObject();
		ois.close();
		if(smo.equals(SMOTOCHECK))
			System.out.println("bingo");
		return smo;
	}

	public SMO setSVMParameters(int option) {
		SMO smo = new SMO();
		if (option == 1) {
			RBFKernel rbf = new RBFKernel();
			smo.setKernel(rbf);
		}
		System.out.println("C: " + smo.getC());
		System.out.println("Epsilon: " + smo.getEpsilon());
		System.out.println("Kernel: " + smo.getKernel());
		try {
			weka.core.SerializationHelper.write(InstanceDataParser.SMO_MODEL_SERIALIZED_FILE, smo);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		SMOTOCHECK=smo;
		return smo;
	}

	public double[] crossTrainAndEvaluate(SMO smo, Instances data, boolean isTest) {
		double truePositive = 0.0, trueNegative = 0.0, falsePositive = 0.0, falseNegative = 0.0;
		double[] Result = null;
		double Q2[] = new double[1];
		try {
			for (int j = 0; j < data.numInstances(); j++) {
				double predictedClass = smo.classifyInstance(data.instance(j));
				Instance everyInstance = data.instance(j);
				String trueClass = everyInstance.stringValue(everyInstance.classIndex());
				if (((int) predictedClass == 0) && (trueClass.toString().equals("+"))) {
					truePositive++;
				} else if (((int) predictedClass == 1) && (trueClass.toString().equals("-"))) {
					trueNegative++;
				} else if (((int) predictedClass == 1) && (trueClass.toString().equals("+"))) {
					falsePositive++;
				} else if (((int) predictedClass == 0) && (trueClass.toString().equals("-"))) {
					falseNegative++;
				}

			}
			if (isTest) {
				Result = calculateTestResult(truePositive, trueNegative, falseNegative, falsePositive);
				return Result;
			} else {
				Q2 = calculateCrosstrainResult(truePositive, trueNegative, falseNegative, falsePositive);
				return Q2;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public double[] calculateCrosstrainResult(double TP, double TN, double FN, double FP) {
		double[] Q2 = new double[1];
		double correct = TP + TN;
		double incorrect = FN + FP;
		Q2[0] = 100.0 * ((correct) / (correct + incorrect));
		return Q2;
	}

	public double[] calculateTestResult(double TP, double TN, double FN, double FP) {
		double[] result = new double[11];
		result[0] = TP;
		result[1] = TN;
		result[2] = FN;
		result[3] = FP;
		result[4] = TP + TN; // correct
		result[5] = FN + FP; // incorrect
		result[6] = 100.0 * ((result[4]) / (result[4] + result[5]));// Q2
		result[7] = 100.0 * (TP / (TP + FP)); // accuracyPositive
		result[8] = 100.0 * (TN / (TN + FN)); // accuracyNegative
		result[9] = 100.0 * (TP / (TP + FN)); // coveragePositive
		result[10] = 100.0 * (TN / (TN + FP)); // coverageNegative
		return result;
	}
}